package com.zyl.blog.tasks.aspect;

import cn.hutool.core.util.IdUtil;
import com.alibaba.nacos.common.utils.ExceptionUtil;
import com.zyl.blog.common.domain.task.bean.TaskLogInfo;
import com.zyl.blog.common.exception.BlogException;
import com.zyl.blog.common.utils.Extreme.LogUtil;
import com.zyl.blog.tasks.annotation.TaskLog;
import com.zyl.blog.tasks.enums.TaskStatusEnum;
import com.zyl.blog.tasks.schedule.abstracts.AbstractScheduledTask;
import com.zyl.blog.tasks.service.TaskAlertService;
import com.zyl.blog.tasks.service.impl.ITaskLogService;
import com.zyl.blog.tasks.utils.AsyncUtil;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * @version v1.0
 * @ProjectName: Personal_Blog_Server
 * @Package: com.zyl.blog.tasks.aspect
 * @ClassName: TaskLogAspect
 * @Description: 任务日志AOP切面，记录任务执行情况
 * @Author FinalFantasy
 * @Date 2025/8/12-16:05
 */
@Aspect
@Component
@RequiredArgsConstructor
public class EnhancedTaskLogAspect {

    private static final String TASK_TRACE_ID = "task_trace_id";
    private final ITaskLogService taskLogService;
    private final TaskAlertService taskAlertService;
    private final AsyncUtil asyncUtil;

    /**
     * 切入点：所有标记了@TaskLog注解的方法
     * 1. 匹配所有标注了@TaskLog注解的类中的方法
     * 2. 或者匹配所有标注了@TaskLog注解的方法
     */
    @Pointcut("@within(com.zyl.blog.tasks.annotation.TaskLog) || @annotation(com.zyl.blog.tasks.annotation.TaskLog)")
    public void taskLogPointcut() {

    }

    /**
     * 环绕通知：记录任务执行全生命周期日志
     */
    @Around("taskLogPointcut() && execution(* com.zyl.blog.tasks.schedule..*.execute(..))")
    public Object aroundTaskExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        /* 初始化追踪ID，用于日志关联 */
        String traceId = MDC.get(TASK_TRACE_ID);
        if (StringUtils.isEmpty(traceId)) {
            traceId = IdUtil.getSnowflakeNextIdStr();
            MDC.put(TASK_TRACE_ID, traceId);
        }

        /* 获取注解信息 - 先从方法获取，如果没有再从类上获取 */
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        TaskLog taskLogAnnotation = methodSignature.getMethod().getAnnotation(TaskLog.class);

        /* 如果方法上没有注解，尝试从类上获取 */
        if (taskLogAnnotation == null) {
            taskLogAnnotation = joinPoint.getTarget().getClass().getAnnotation(TaskLog.class);
        }

        /* 如果仍然没有获取到注解，直接执行方法不记录日志 */
        if (taskLogAnnotation == null) {
            return joinPoint.proceed();
        }

        /* 初始化日志实体 */
        long startTime = System.currentTimeMillis();
        TaskLogInfo logEntity = buildInitialLog(taskLogAnnotation, traceId, joinPoint);
        TaskStatusEnum finalStatus = TaskStatusEnum.INITIALIZED;

        try {
            /* 记录任务开始状态 */
            logEntity.setStatus(TaskStatusEnum.RUNNING.getCode());
            logEntity.setStatusMsg(TaskStatusEnum.RUNNING.getDesc());
            if (taskLogAnnotation.persist()) {
                asyncUtil.execute(() -> taskLogService.save(logEntity));
            }

            /* 执行任务 */
            Object result = joinPoint.proceed();

            /* 任务成功执行 */
            finalStatus = TaskStatusEnum.SUCCESS;
            logEntity.setResultSummary(result != null ?
                    StringUtils.abbreviate(result.toString(), 500) : "执行成功");
            return result;

        } catch (Exception ex) {
            /* 处理执行异常 */
            finalStatus = handleTaskException(taskLogAnnotation, ex);
            logEntity.setErrorStack(ExceptionUtil.getStackTrace(ex));
            logEntity.setErrorCode(resolveErrorCode(ex));
            throw ex;

        } finally {
            /* 完成日志记录 */
            completeTaskLogging(taskLogAnnotation, traceId, startTime, finalStatus, logEntity);
            MDC.remove(TASK_TRACE_ID);
        }
    }

    /**
     * 构建初始日志实体
     */
    private TaskLogInfo buildInitialLog(TaskLog annotation, String traceId, ProceedingJoinPoint joinPoint) {
        return new TaskLogInfo()
                .setTraceId(traceId)
                .setTaskId(resolveTaskId(annotation, joinPoint))
                .setTaskName(resolveTaskName(annotation, joinPoint))
                .setTaskType(annotation.taskType())
                .setOperator(resolveOperator(annotation, joinPoint))  // 修改：增加joinPoint参数
                .setTriggerMode(annotation.triggerMode().name())
                .setStatus(TaskStatusEnum.INITIALIZED.getCode())
                .setStatusMsg(TaskStatusEnum.INITIALIZED.getDesc())
                .setExecuteStart(LocalDateTime.now());
    }

    /**
     * 完成任务日志记录
     */
    private void completeTaskLogging(TaskLog annotation, String traceId, long startTime,
                                     TaskStatusEnum status, TaskLogInfo logInfo) {
        /* 计算耗时并更新时间 */
        long costTime = System.currentTimeMillis() - startTime;
        logInfo
                .setTaskId(traceId)
                .setExecuteDuration(costTime)
                .setExecuteEnd(LocalDateTime.now())
                .setStatus(status.getCode())
                .setStatusMsg(status.getDesc());

        /* 异步更新日志到数据库 */
        if (annotation.persist()) {
            asyncUtil.execute(() -> taskLogService.updateById(logInfo));
        }

        /* 打印结构化日志 */
        printStructuredLog(logInfo, status);

        /* 发送告警（如果需要） */
        if (status.isFailed() && annotation.enableAlert()) {
            asyncUtil.execute(() -> taskAlertService.sendTaskAlert(logInfo));
        }
    }

    /**
     * 打印结构化日志
     */
    private void printStructuredLog(TaskLogInfo log, TaskStatusEnum status) {
        String logTemplate = getLogTemplateByStatus(status);

        if (status.isFailed()) {
            LogUtil.error(
                    logTemplate,
                    null,
                    log.getTraceId(),
                    log.getTaskId(),
                    log.getTaskName(),
                    log.getTaskType(),
                    log.getOperator(),
                    log.getTriggerMode(),
                    status.getDesc(),
                    log.getExecuteDuration(),
                    log.getExecuteStart(),
                    log.getExecuteEnd(),
                    log.getErrorCode(),
                    StringUtils.abbreviate(log.getErrorStack(), 200)
            );
        } else {
            LogUtil.info(
                    logTemplate,
                    log.getTraceId(),
                    log.getTaskId(),
                    log.getTaskName(),
                    log.getTaskType(),
                    log.getOperator(),
                    log.getTriggerMode(),
                    status.getDesc(),
                    log.getExecuteDuration(),
                    log.getExecuteStart(),
                    log.getExecuteEnd()
            );
        }
    }

    /**
     * 根据任务状态获取日志模板
     */
    private String getLogTemplateByStatus(TaskStatusEnum status) {
        String baseTemplate = """
                \n===================================== Task Execution Report =====================================
                | Trace ID       : {}
                | Task ID        : {}
                | Task Name      : {}
                | Task Type      : {}
                | Operator       : {}
                | Trigger Mode   : {}
                | Status         : {}
                | Cost Time      : {}ms
                | Start Time     : {}
                | End Time       : {}
                """;

        /* 失败状态添加错误信息 */
        if (status.isFailed()) {
            baseTemplate += """
                    | Error Code     : {}
                    | Error Summary  : {}
                    """;
        }

        baseTemplate += "===============================================================================================";
        return baseTemplate;
    }

    /**
     * 处理任务执行异常
     */
    private TaskStatusEnum handleTaskException(TaskLog annotation, Exception ex) {
        if (ex instanceof java.util.concurrent.TimeoutException) {
            return TaskStatusEnum.TIMEOUT;
        } else if (annotation.retryTimes() > 0) {
            return TaskStatusEnum.RETRYING;
        } else {
            return TaskStatusEnum.FAILED;
        }
    }

    /**
     * 解析错误代码
     */
    private String resolveErrorCode(Exception ex) {
        // 这里可以根据实际业务异常类型解析错误码
        if (ex instanceof BlogException be) {
            return be.getMessage();
        }
        return "SYSTEM_ERROR";
    }

    /**
     * 解析任务ID
     */
    private String resolveTaskId(TaskLog annotation, ProceedingJoinPoint joinPoint) {
        if (StringUtils.isNotEmpty(annotation.taskId())) {  // 修改：检查条件反转
            return annotation.taskId();
        }
        /* 从目标对象中获取任务ID（假设任务类实现了getId()方法） */
        Object target = joinPoint.getTarget();
        try {
            return target.getClass().getMethod("getTaskId").invoke(target).toString();
        } catch (Exception e) {
            return joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
        }
    }


    /**
     * 解析任务名称
     */
    private String resolveTaskName(TaskLog annotation, ProceedingJoinPoint joinPoint) {
        if (StringUtils.isNotEmpty(annotation.taskName())) {  // 修改：检查条件反转
            return annotation.taskName();
        }
        /* 从目标对象中获取任务名称 */
        Object target = joinPoint.getTarget();
        try {
            return target.getClass().getMethod("getTaskName").invoke(target).toString();
        } catch (Exception e) {
            return joinPoint.getSignature().getName();
        }
    }

    /**
     * 解析操作者（任务所属用户）
     */
    private String resolveOperator(TaskLog annotation, ProceedingJoinPoint joinPoint) {
        /* 如果注解中指定了操作者，直接使用 */
        if (StringUtils.isNotEmpty(annotation.operator())) {
            return annotation.operator();
        }

        /* 尝试从任务实例中获取用户名 */
        try {
            Object target = joinPoint.getTarget();
            if (target instanceof AbstractScheduledTask) {
                return ((AbstractScheduledTask) target).getUsername();
            }
        } catch (Exception e) {
            LogUtil.warn("获取任务操作者失败: {}", e.getMessage());
        }

        return "SYSTEM";
    }

}
